﻿using System;
using System.Globalization;
using System.Collections;
using System.Runtime.Serialization;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;

namespace Vowei.Core.Models
{
    public class RecurrenceSetting : ITable
    {
        public RecurrenceSetting()
            : this(DateTime.Now)
        {
        }

        public RecurrenceSetting(DateTime startDate)
        {
            CreateDate = startDate;
        }

        public Task Task { get; set; }

        /// <summary>
        /// 重复任务的终止时间
        /// </summary>
        [DataMember]
        [Display(Name = "重复终止时间")]
        public DateTime? RecursiveEndDate { get; set; }

        public string CultureCode { get; set; }

        /// <summary>
        /// 用户文化方面的日历
        /// </summary>
        /// <remarks>默认是公历</remarks>
        private Calendar _calendar;
        public Calendar Calendar
        {
            get
            {
                if (_calendar == null)
                    _calendar = new CultureInfo(CultureCode).Calendar;

                return _calendar;
            }
        }

        public DateTime CreateDate { get; set; }

        public int YearOccurrence { get; private set; }

        public int MonthOccurrence { get; private set; }

        public int WeekOccurrence { get; private set; }

        public int DayOccurrence { get; private set; }
        
        public BitArray RecurWeekDays { get; private set; }

        public int RecurMonth { get; private set; }

        public int RecurDay { get; private set; }
        
        public RecurrencePattern Pattern { get; set; }

        public void OnAllWeekDays()
        {
            WeekOccurrence = 1;
            RecurWeekDays = new BitArray(new bool[] {
                false, // 星期日
                true,  // 星期一
                true,  // 星期二
                true,  // 星期三
                true,  // 星期四
                true,  // 星期五
                false  // 星期日
            });
        }

        public void OnDay(int day)
        {
            RecurDay = day;
        }

        public RecurrenceSetting OnMonth(int month)
        {
            RecurMonth = month;
            return this;
        }

        public RecurrenceSetting OnWeekDays(params DayOfWeek[] weekdays)
        {
            if (weekdays == null)
                throw new ArgumentNullException("weekdays");
            if (weekdays.Length > 7)
                throw new ArgumentOutOfRangeException("weekdays的长度不能大于7！");

            RecurWeekDays = new BitArray(new bool[] {
                false, // 星期日
                false,  // 星期一
                false,  // 星期二
                false,  // 星期三
                false,  // 星期四
                false,  // 星期五
                false  // 星期日
            });
            foreach (var weekday in weekdays)
            {
                RecurWeekDays[(int)weekday] = true;
            }

            return this;
        }

        public RecurrenceSetting EveryDays(int times)
        {
            DayOccurrence = times;
            OnDay(CreateDate.Day);
            return this;
        }

        public RecurrenceSetting EveryWeeks(int times)
        {
            WeekOccurrence = times;
            OnWeekDays(CreateDate.DayOfWeek);
            return this;
        }

        public RecurrenceSetting EveryMonths(int times)
        {
            MonthOccurrence = times;
            OnMonth(CreateDate.Month);
            return this;
        }

        public RecurrenceSetting EveryYears(int times)
        {
            YearOccurrence = times;
            EveryMonths(12 * YearOccurrence);
            OnDay(CreateDate.Day);
            return this;
        }

        public RecurrenceSetting DeleteOccurrencyAt(DateTime date)
        {
            throw new NotImplementedException();
        }

        public RecurrenceSetting AddException(DateTime date)
        {
            throw new NotImplementedException();
        }

        public Guid ID
        {
            get;
            set;
        }

        public bool HasOccurrence(DateTime date)
        {
            if (RecursiveEndDate.HasValue && date > RecursiveEndDate.Value)
                return false;
            if (date < CreateDate)
                return false;
            if (date.Year == CreateDate.Year && date.Month == CreateDate.Month &&
                 date.Day == CreateDate.Day && date.Hour == CreateDate.Hour &&
                 date.Minute == CreateDate.Minute && date.Second == CreateDate.Second)
                return true;

            switch (Pattern)
            {
                case RecurrencePattern.Daily:
                    return HasDailyOccurrence(date);

                case RecurrencePattern.Weekly:
                    return HasWeeklyOccurrence(date);

                case RecurrencePattern.Monthly:
                    return HasMonthlyOccurrence(date);

                case RecurrencePattern.Yearly:
                    return HasYearlyOccurrence(date);

                default:
                    throw new NotImplementedException(string.Format("尚不支持针对重复模式：{0}.", Pattern));
            }
        }

        private bool HasYearlyOccurrence(DateTime date)
        {
            var years = date.Year - CreateDate.Year;
            var result = years % YearOccurrence == 0;
            result = result && HasMonthlyOccurrence(date);

            return result;
        }

        private bool HasMonthlyOccurrence(DateTime date)
        {
            var years = date.Year - CreateDate.Year - 1;
            var months = date.Month + 12 - CreateDate.Month;
            months += years <= 0 ? 0 : years * 12;

            var result = months % MonthOccurrence == 0;
            if (WeekOccurrence != 0)
            {
                Debug.Assert(DayOccurrence == 0);
                var dayofweek = date.DayOfWeek;

                // 获取当前月份第一天的星期数
                var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
                // TODO: 看看后两个参数是否需要考虑国际化因素
                var weekOfFirstDayInMonth = Calendar.GetWeekOfYear(firstDayOfMonth, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
                var weekOfDate = Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
                
                result = result && RecurWeekDays[(int)dayofweek] && ((weekOfDate - weekOfFirstDayInMonth + 1) % WeekOccurrence) == 0;
            }
            else
            {
                result = result && date.Day == RecurDay;
            }

            return result;
        }

        private bool HasWeeklyOccurrence(DateTime date)
        {
            if (RecurWeekDays == null)
                throw new InvalidOperationException("指定任务重复模式为每周重复的话，需要指明在星期几重复！");

            var dayofweek = date.DayOfWeek;
            var span = date - CreateDate;
            var weeks = (int)(span.TotalDays / 7);
            
            // 修复CreateDate是星期五，但是date是星期三的情况。
            if (CreateDate.DayOfWeek > dayofweek)
                weeks++;

            return RecurWeekDays[(int)dayofweek] && (weeks % WeekOccurrence == 0);
        }

        private bool HasDailyOccurrence(DateTime date)
        {
            // 说明是按照工作日来重复的
            if (WeekOccurrence == 1)
            {
                Debug.Assert(DayOccurrence == 0);
                var dayofweek = date.DayOfWeek;
                return RecurWeekDays[(int)dayofweek];
            }
            else
            {
                var span = date - CreateDate;
                return (int)span.TotalDays % DayOccurrence == 0;
            }
        }
    }
}